package system;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;

import card.Card;
import card.CardInfo;
import card.CardTerm;
import card.CardType;
import card.StartCardRideQuantity;
import card.State;
import collections.map.MyMap;
import collections.map.MyMap.Entry;
import collections.map.hash_map.MyHashMap;
import exceptions.NonWorkingCardException;
import exceptions.WrongCardTypeException;

public class System {
	
	private MyMap<Long, CardInfo> cardInfoMap = new MyHashMap<>();
	private MyMap<StatisticsKey, CardInfo> statistics = new MyHashMap<>();
	private static System instance = new System();
	private CardFactory cardFactory = new CardFactory();
	private long lastIdValue;
	private System() {
	}
	
	public static System getInstance() {
		return instance;
	}
	
	public CardInfo getCardInfo(Long card) {
		return cardInfoMap.get(card);
	}
	
	public boolean isCardWorking(Long card) {
		return cardInfoMap.get(card).getState() == State.WORKING;
	}
	
	public void addToStatistics(Long card) {
		CardInfo currentCard = getCardInfo(card);
		statistics.put(new StatisticsKey(new GregorianCalendar(), currentCard.getCardID()), cardInfoMap.get(card));
	}
	
	private class StatisticsKey {
		Calendar date;
		long cardID;
		
		StatisticsKey(Calendar date, long cardID) {
			this.date = date;
			this.cardID = cardID;
		}
		
		long getCardID() {
			return cardID;
		}
		
		Calendar getDate() {
			return date;
		}
	}
	
	public Card createCard(String cardNumber, CardType type, CardTerm term, StartCardRideQuantity startQuantity) {
		Card result = cardFactory.createCard(cardNumber, type, term, startQuantity);	
		return result;
	}
	
	public void decreaseRideQuantity(Long card) throws NonWorkingCardException {
		CardInfo cardInfo = cardInfoMap.get(card);
		if(cardInfo.getState() == State.NON_WORKING || cardInfo.getRideQuantity() == 0 ||
				(cardInfo.getEndWorkingDate().compareTo(new GregorianCalendar()) > 0)) {
			throw new NonWorkingCardException();
		} else {
			cardInfoMap.get(card).decreaseRideQuantity();
		}
	}
	
	public void increaseRideQuantity(Long card, int count) throws WrongCardTypeException {
		CardInfo cardInfo = getCardInfo(card);
		if(cardInfo.getCardType() == CardType.SIMPLE_CARD) {
			cardInfoMap.get(card).increaseRideQuantity(count);
		} else {
			throw new WrongCardTypeException();
		}
	}
	
	public void changeCardState(Long card, State state) {
		cardInfoMap.get(card).setCardState(state);
	}
	
	public MyMap<StatisticsKey, CardInfo> getStatistics() {
		MyMap<StatisticsKey, CardInfo> result = new MyHashMap<>();
		Iterator<Entry<StatisticsKey, CardInfo>> iter = statistics.entryIterator();
		while(iter.hasNext()) {
			Entry<StatisticsKey, CardInfo> entry = iter.next();
			result.put(entry.getKey(), entry.getValue());
		}
		return result;
	}
	
	public class CardFactory {
		
		Card createCard(String cardNumber, CardType type, CardTerm term, StartCardRideQuantity startQuantity) {
			Card result = new Card(generateID(), cardNumber);
			CardInfo cardInfo = new CardInfo(result.getCardID(), result.getCardNumber(), type, term, startQuantity,
					State.WORKING, new GregorianCalendar());
			cardInfoMap.put(result.getCardID(), cardInfo);
			return result;
		}
		
	}
	
	private long generateID() {
		return lastIdValue++;
	}
	
}
